import {rectIsCrossOrContain} from 'rect-rel';

/*
# 常用转换器

## 转换器接收的类型
(distance:Coord,index:number,distanceArr:[Coord],itemRect :Rect,itemElement?:Element,containerElement?:Element)=>Coord

@this   :Object     用于保存各个距离坐标对象所共享的数据； this 的值会被作为 distanceArr.publicData 的值传给下一个转换器；
@param distance : {x:number,y:number}    项目锚点与第index个影响锚点之间的距离坐标
@param index : number       当前影响锚点的序号
@param distanceArr : [{x:number,y:number}]      项目锚点与所有影响锚点的距离坐标数组，该数组有个 publicData 属性，publicData 属性中存储的是各个距离坐标对象共享的数据；
@param itemRect :{x:number,y:number,width:number,height:number}    项目元素的位置和长宽信息
@param itemElement ?:Element    项目元素的Dom节点
@param containerElement ?:Element   项目元素的容器的Dom节点；
@returns {x:number,y:number,...other}   转换后的距离坐标对象；


## 注意
- 转换器会按转换器数组中的顺序调用，上个转换器的返回值，会作为下个转换器的distance；
- 转换器中返回的对象必须包含 `x` 和 `y` 属性；
- 对于距离坐标对象的私有数据应该保存在距离坐标对象中；
- 对于不属于任何距离坐标的数据应该保存在 转换器中的 this 里；this 里的数据会被作为共享数据储存在 distanceArr.publicData 中；
- 通用的转换器应该把转换后的值与传入的 distance 合并，以达到覆盖distance的 `x` 和 `y` 属性，同时又能保留distance中的其它属性值；
   ```
   function transformeFun(distance) {
       //转换过程省略...

       let transformedResult = ...;     //转换结果

       let newDistance = { ...distance, ...transformedResult };     //合并 原距离坐标对象 和 转换结果
       return newDistance; 返回合并后的对象
   }
   ```
- 如果需要实现转换器的分支，则需要把转换的结果保存在 this中 或者 距离坐标对象中除 `x` 和 `y` 属性之外的其它属性中；
*/



/*
# 绝对值转换器
*/
export function abs(distance) {
    let abs = Math.abs;

    let transformedResult = {
        x: abs(distance.x),
        y: abs(distance.y)
    };

    let newDistance = { ...distance, ...transformedResult };
    return newDistance;
}


/*
# 放大转换器生成器
@param referencer ?: {x:number = 1,y:number = 1}  参考数据，相对数据
@param effectRange ?: {x:number = Number.POSITIVE_INFINITY,y:number = Number.POSITIVE_INFINITY}  有效范围
*/
export function magnifierCreater(referencer = { x: 1, y: 1 }, effectRange = { x: Number.POSITIVE_INFINITY, y: Number.POSITIVE_INFINITY }) {

    let referencerX = referencer.x || 1;
    let referencerY = referencer.y || 1;

    let rangeX = effectRange.x || Number.POSITIVE_INFINITY;
    let rangeY = effectRange.y || Number.POSITIVE_INFINITY;


    let magnifierTransform = function (distance) {

        let abs = Math.abs;
        let resultX = distance.x;
        let resultY = distance.y;

        let effectResultX = abs(resultX) < rangeX ? resultX : rangeX;
        let effectResultY = abs(resultY) < rangeY ? resultY : rangeY;

        let magnifierX = 1 - (effectResultX / referencerX);
        let magnifiery = 1 - (effectResultY / referencerY);

        let transformedResult = { x: magnifierX, y: magnifiery };

        let newDistance = { ...distance, ...transformedResult };
        return newDistance;
    };

    return magnifierTransform;
}







/**
 * 计算线性缩小比例
 * 
 * @param  x :number    变量
 * @param  base  :number   缩小比例的参考值；
 * @returns  缩小的比例；当x 的绝对值 在 0 到 base 区间变化，返回值会在 1 到 0 之间变化；
 */
function linearShrink(x, base) {
    return 1 - Math.abs(x) / base;
}


/**
 * 计算2次幂缩小比例
 * 
 * @param  x :number    变量
 * @param  base  :number   缩小比例的参考值；
 * @returns  缩小的比例；当x 的绝对值 在 0 到 base 区间变化，返回值会在 1 到 0 之间变化；
 */
function pow2Shrink(x, base) {
    return 1 - Math.pow(x / base,2);
}







/*
# 钻洞转换器生成器
@param radius ?: {x:number,y:number}  洞的半径
@param drillDistance ?: {x:number,y:number}  开始钻孔的距离
@param effectRange ?: {x:number = Number.POSITIVE_INFINITY,y:number = Number.POSITIVE_INFINITY}  有效范围，影响范围
*/
export function drillHoleCreater(radius = { x: 0, y: 0 }, drillDistance = {x:0,y:0}, effectRange = { x: Number.POSITIVE_INFINITY, y: Number.POSITIVE_INFINITY }) {

    let radiusX = radius.x || 0;
    let radiusY = radius.y || 0;

    let drillX = drillDistance.x || 0;
    let drillY = drillDistance.y || 0;

    let rangeX = effectRange.x || Number.POSITIVE_INFINITY;
    let rangeY = effectRange.y || Number.POSITIVE_INFINITY;

    let abs = Math.abs;
    let pow = Math.pow;



    /**
     * 计算缩小比例
     * 
     * @param distance  : number    在滑动方向的距离
     * @param drillDistance  : number   钻洞动画在滑动方向的动画长度
     * @returns 缩小的比例
     */
    function moveCurve(distance,drillDistance){
        return pow2Shrink(distance,drillDistance);
    }
    


    let drillHoleTransform = function (distance) {


        let distanceX = distance.x;
        let distanceY = distance.y;

        let offsetX = 0;
        let offsetY = 0;

        let scaleX = 1;
        let scaleY = 1;

        if (abs(distanceX) < drillX  &&  abs(distanceY) < rangeY){
            scaleX = pow(distanceX/drillX,2);
            offsetY =  (distanceY/rangeY * radiusY - distanceY) * moveCurve(distanceX,drillX);
        }
        

        if (abs(distanceY) < drillY  &&  abs(distanceX) < rangeX){
            scaleY = pow(distanceY/drillY,2);
            offsetX =  (distanceX/rangeX * radiusX - distanceX) * moveCurve(distanceY,drillY);
        }



        let transformedResult = { offset:{x: offsetX, y: offsetY} ,scale:{x:scaleX,y:scaleY} };

        let newDistance = { ...distance, ...transformedResult };
        return newDistance;
    };

    return drillHoleTransform;
}





/**
 * 根据三角形的斜边和一个直角边，计算另一个直角边的长；
 * 
 * @param {any} hypotenuse 
 * @param {any} aRightAngleEdge 
 * @returns 
 */
function computeRightAngleEdge(hypotenuse,aRightAngleEdge){
    let pow = Math.pow;
    return Math.sqrt(pow(hypotenuse,2) - pow(aRightAngleEdge,2));
}


/*
# 直角邻边转换器生成器
@param hypotenuse ?: {x:number,y:number}  直角三角形斜边的长
@param overflowValue ?: number  当坐标距离的绝对值大于 hypotenuse 时的转换结果值；默认为原始坐标距离；
*/
export function rightAngleEdgeCreater(hypotenuse = { x: 0, y: 0 },overflowValue) {

    let hypotenuseX = hypotenuse.x || 0;
    let hypotenuseY = hypotenuse.y || 0;

    let abs = Math.abs;
    let pow = Math.pow;


    
    let rightAngleEdgeTransform = function (distance) {

        let distanceX = distance.x;
        let distanceY = distance.y;

        let rightAngEdgeX = distanceX;
        let rightAngEdgeY = distanceY;

        if (overflowValue != undefined){
            rightAngEdgeX = overflowValue;
            rightAngEdgeY = overflowValue;
        }

        if (abs(distanceX) <= hypotenuseX ){
            rightAngEdgeX = computeRightAngleEdge(hypotenuseX,distanceX);
        }
        if (abs(distanceY) <= hypotenuseY ){
            rightAngEdgeY = computeRightAngleEdge(hypotenuseY,distanceY);
        }



        let transformedResult = {x: rightAngEdgeX, y: rightAngEdgeY};

        let newDistance = { ...distance, ...transformedResult };
        return newDistance;
    };

    return rightAngleEdgeTransform;
}








/**
 * 三角形斜边斜边转换器
 * @returns {{x: number, y: number}}    返回以distance的x和y为直角边的三角形的斜边的长；
 *  # 注意：斜边的长存放在x中；
 */
export function hypotenuse(distance) {
    let pow = Math.pow;
    let hypotenuseLength = Math.sqrt(pow(distance.x, 2) + pow(distance.y, 2));

    let transformedResult = {
        x: hypotenuseLength,
        y: 0
    };


    let newDistance = { ...distance, ...transformedResult };
    return newDistance;
}


/**
 * 判断是否包含项目矩形的转换器生成器
 * @param compriseReact :{x:number,y:number,width:number,height:number}  包含者的矩形
 *
 * # 注意
 * 判断结果保存在 distanceArr.publicData.isCrossOrContainItemRect
 */
export function isCrossOrContainItemRectCreater(compriseReact) {

    let isCrossOrContainItemRect = function (distance,index,distanceArr,itemRect) {
        this.isCrossOrContainItemRect = rectIsCrossOrContain(compriseReact, itemRect);
        return distance;
    };

    return isCrossOrContainItemRect;
}



/**
 * 判断Affecter的视口是否包含项目矩形的转换器
 *
 * # 注意
 * 判断结果保存在 distanceArr.publicData..affecterIsCrossOrContainItemRect
 */
export function affecterIsCrossOrContainItemRect(distance,index,distanceArr,itemRect, itemElement, containerElement) {

    if (containerElement) {

        let compriseReact = {
            x: containerElement.scrollLeft,
            y: containerElement.scrollTop,
            width: containerElement.clientWidth,
            height: containerElement.clientHeight
        };
        this.affecterIsCrossOrContainItemRect = rectIsCrossOrContain(compriseReact, itemRect);
    }

    return distance;
}






/**
 * 寻找距离坐标中最大的 x 和 y
 *
 * # 注意 
 * 结果保存在 publicData.maxX 和 publicData.maxY 中
 */
export function maxXYIndex(distance,index,distanceArr,itemRect, itemElement, containerElement) {

    if (index === 0){

        let xArr = [];
        let yArr = [];
        distanceArr.forEach(function(distance, index){
            xArr[index] = distance.x;
            yArr[index] = distance.y;
        });
        let max = Math.max;
        this.maxX = max(...xArr);
        this.maxY = max(...yArr);
    
    }
        
    return distance;

}




/**
 * 寻找距离坐标中最小的 x 和 y
 *
 * # 注意 
 * 结果保存在 publicData.minX 和 publicData.minY 中
 */
export function minXYIndex(distance, index, distanceArr, itemRect, itemElement, containerElement) {

    if (index === 0) {

        let xArr = [];
        let yArr = [];
        distanceArr.forEach(function (distance, index) {
            xArr[index] = distance.x;
            yArr[index] = distance.y;
        });
        let min = Math.min;
        this.minX = min(...xArr);
        this.minY = min(...yArr);

    }

    return distance;

}

    
    